﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace AnaxibiaStudios.Smug.Advanced
{
  public class PreparedEvent
  {
    public static PreparedEvent<Action> LoadFrom(object proxy, Action<Action> eventSnatcher)
    {
      var result = new PreparedEvent<Action>();
      result.Target = ProxyRepository.CastProxy(proxy);

      var type = proxy.GetType();
      var ev = type.GetEvents().First();

      var meta = Internal.SourceBuilder.BuildInterfaceWrappedMethod(ev.EventHandlerType);
      var compiler = new Internal.Compiler();
      var wrapType = compiler.Compile(typeof(object), meta.Item1, meta.Item2, Internal.Constants.DotNet4_0);

      var methodInfo = wrapType.GetMethod(meta.Item3, Internal.Constants.Flags | BindingFlags.Static);
      var del = Delegate.CreateDelegate(typeof(Action), methodInfo);
      var act = (Action)del;

      var snatched = AllEvents(proxy).FirstOrDefault(eventInfo => EventHasSnatch(eventInfo, act));

      if (snatched == null)
      {
        // throw 'you didn't pass the same object on which you added the snatch'
      }

      throw new NotImplementedException();

      //var meta = GetEventMeta(proxy, snatched);

      //result.Raise = () => emeta.Raise(new object[] { });
      //result.Target = iproxy;

      //return result;
    }

    public static bool EventHasSnatch(EventInfo einfo, Delegate snatcher)
    {
      return false;
    }

    public static IEnumerable<EventInfo> AllEvents(object target)
    {
      yield break;
    }

    public static EventMeta GetEventMeta(object target, EventInfo eventInfo)
    {
      //var eventInfo = declaringType.GetEvent(eventName, AnaxibiaStudios.Smug.Advanced.Constants.Flags);

      //if (eventInfo == null)
      //{
        // no such event
      //}

      var eventField = eventInfo.DeclaringType.GetField(eventInfo.Name, AnaxibiaStudios.Smug.Internal.Constants.Flags);
      var meta = new EventMeta(eventInfo);

      meta.Raise = args =>
      {
        var einst = eventField.GetValue(target) as Delegate;

        if (einst == null)
        {
          return null; //do nothing, no subscribers
        }

        var list = einst.GetInvocationList();

        object result = null;
        foreach (var del in list)
        {
          result = del.Method.Invoke(del.Target, args);
        }

        return result;
      };

      var adder = eventInfo.GetAddMethod(true) ?? eventInfo.GetAddMethod(false);
      meta.Add = del =>
      {
        adder.Invoke(target, new object[] { del });
      };

      var remover = eventInfo.GetRemoveMethod(true) ?? eventInfo.GetRemoveMethod(false);
      meta.Remove = del =>
      {
        remover.Invoke(target, new object[] { del });
      };

      return meta;
    }

    public static Delegate[] GetEventSubscribers(object target, string eventName)
    {
      Type t = target.GetType();

      do
      {
        FieldInfo[] fia = t.GetFields(
             BindingFlags.Static |
             BindingFlags.Instance |
             BindingFlags.NonPublic);

        foreach (FieldInfo fi in fia)
        {
          if (fi.Name == eventName)
          {
            Delegate d = fi.GetValue(target) as Delegate;
            if (d != null)
              return d.GetInvocationList();
          }
        }

        t = t.BaseType;

      } while (t != null);
      return new Delegate[] { };
    }
  }

  public class PreparedEvent<TSignature>
  {
    public IProxy Target { get; set; }
    public TSignature Raise { get; set; }
    public SubscriberAccess Subscribers { get; set; }
    public void Remove(TSignature handler) { }
    public void Add(TSignature handler) { }

    public PreparedEvent()
    {
      Subscribers = new SubscriberAccess();
    }

    public class SubscriberAccess : IList<TSignature>
    {
      public int IndexOf(TSignature item) { throw new NotImplementedException(); }
      public void Insert(int index, TSignature item) { throw new NotImplementedException(); }
      public void RemoveAt(int index) { throw new NotImplementedException(); }
      public TSignature this[int index] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
      public void Add(TSignature item) { throw new NotImplementedException(); }
      public void Clear() { throw new NotImplementedException(); }
      public bool Contains(TSignature item) { throw new NotImplementedException(); }
      public void CopyTo(TSignature[] array, int arrayIndex) { throw new NotImplementedException(); }
      public int Count { get { throw new NotImplementedException(); } }
      public bool IsReadOnly { get { throw new NotImplementedException(); } }
      public bool Remove(TSignature item) { throw new NotImplementedException(); }
      public IEnumerator<TSignature> GetEnumerator() { throw new NotImplementedException(); }
      System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
    }
  }
}
